#ifndef EPUBZLIB_ZIP_BASIC_HPP
#define EPUBZLIB_ZIP_BASIC_HPP

SJR_NAMESPACE_BEGIN

class EpubZipBasic {
public:
  class DataSet final {
  public:
    explicit DataSet(std::string const &path) : mNative(path), mName(), mPath() {
      auto &&native = std::filesystem::path(path);
      mName         = native.filename().string();
      mPath         = native.parent_path().string();
    }

    auto name() const -> char const * {
      return mName.c_str();
    }

    auto path() const -> char const * {
      return mPath.c_str();
    }

    auto native() const -> char const * {
      return mNative.c_str();
    }

  private:
    std::string mNative;
    std::string mName;
    std::string mPath;
  };

  struct OnZipResourcesListener {
    virtual ~OnZipResourcesListener() = default;

    virtual void onZipResources(DataSet &dataset, int step) = 0;
  };

  struct OnZipProcessListener {
    virtual ~OnZipProcessListener() = default;

    virtual void onZipProcess(char const *name, int step) = 0;
  };

  EpubZipBasic()
      : mListenersProcess(), mListenerSync(), mListenersPaths(), mPaths(), mPathsSync() {}

  virtual ~EpubZipBasic() = default;

  EpubZipBasic(EpubZipBasic const &) = delete;
  EpubZipBasic &operator=(EpubZipBasic const &) = delete;

  void clean() {
    EpubUniqueLock lock(mPathsSync);
    mPaths.clear();
  }

  void add(std::string const &path) {
    EpubUniqueLock lock(mPathsSync);
    if (!path.empty())
      scanPathByDirctory(mPaths, path);
  }

  virtual void compress(std::string const &path) = 0;

  void registerOnZipProcessListener(OnZipProcessListener *listener) {
    EpubUniqueLock lock(mListenerSync);
    if (listener != nullptr)
      mListenersProcess.emplace_back(listener);
  }

  void unregisterOnZipProcessListener(OnZipProcessListener *listener) {
    EpubUniqueLock lock(mListenerSync);
    if (listener != nullptr)
      mListenersProcess.remove(listener);
  }

protected:
  void registerOnZipResourcesListener(OnZipResourcesListener *listener) {
    EpubUniqueLock lock(mPathsSync);
    if (listener != nullptr)
      mListenersPaths.emplace_back(listener);
  }

  void unregisterOnZipResourcesListener(OnZipResourcesListener *listener) {
    EpubUniqueLock lock(mPathsSync);
    if (listener != nullptr)
      mListenersPaths.remove(listener);
  }

  void callOnZipProcessListener(char const *name, int step) {
    EpubUniqueLock lock(mListenerSync);
    for (auto &&item : mListenersProcess)
      (*item).onZipProcess(name, step);
  }

  void callOnZipResourcesListener() {
    EpubUniqueLock lock(mPathsSync);
    auto &&step = float(0);
    auto &&size = static_cast<float>(mPaths.size());

    for (auto &&dataset : mPaths) {
      ++step;
      for (auto &&item : mListenersPaths)
        (*item).onZipResources(dataset, static_cast<int>((step / size) * 100));
    }
  }

private:
  std::list<OnZipProcessListener *> mListenersProcess;
  EpubMutex mListenerSync;

  std::list<OnZipResourcesListener *> mListenersPaths;
  std::list<DataSet> mPaths;
  EpubMutex mPathsSync;

  static void scanPathByDirctory(std::list<DataSet> &paths, std::string const &path) {
    if (!std::filesystem::exists(path))
      return;

    if (!std::filesystem::is_directory(path)) {
      paths.emplace_back(path);
    } else {
      for (auto &&item : std::filesystem::directory_iterator(path)) {
        if (!std::filesystem::is_directory(item))
          paths.emplace_back(item.path().string());
        else
          scanPathByDirctory(paths, item.path().string());
      }
    }
  }
};

SJR_NAMESPACE_END

#endif//EPUBZLIB_ZIP_BASIC_HPP
